<?php
/**
 * This class stores a collection of key-value option pairs. 
 * 
 * During object construction, some subclasses may need to set additional 
 * options, and a means to consistently access these options later.  This 
 * class collects key-value option pairs, and provides methods to
 * determine if the option exists or to retrieve the option value.
 * 
 * The class should be used to collection and store primitive types such as 
 * strings, integers, or boolean values.  Other types may be stored, but perhaps
 * need to be redesigned to be stored elsewhere.
 * 
 */
class ClassStructure_Property
{
	
	/**
	 * A constant to denote a return exception value for unknown properties
	 * 
	 * @constant string
	 * @final
	 * @staticvar string
	 */
	const RETURN_EXCEPTION = 'return_exception';
	
	/**
	 * An associative array of key-value property pairs
	 *  
	 * @access private
	 * @var array
	 */
	private $arr_properties = array();

	/**
	 * Constructor: sets object properties during object construction
	 * 
	 * @access public
	 * @param mixed $mix_properties An array or object of properties.
	 * @return object
	 */
	public function __construct($mix_properties = array()) {
		
	    if(is_array($mix_properties)) {
	        
			$this->setProperties($mix_properties);
			
		} else if(is_object($mix_properties) && ($mix_properties instanceof ClassStructure_Property)) {
			
		    $this->setProperties($mix_properties->getProperties());
		    
		}
		
	}
    
	/**
	 * Sets a collection of key-value property pairs
	 * 
	 * @access protected
	 * @param array $arr_properties An associative array of key-value property pairs.
	 * @return void
	 */
	protected function setProperties($arr_properties = array()) {
		
        if(is_array($arr_properties)) {
			$this->arr_properties = array_merge($this->arr_properties, $arr_properties);
		}
		
	}
		
	/**
	 * Assigns a specific property, and can enforce overwriting values
	 * 
	 * @access protected
	 * @param string $str_key A valid property key.
	 * @param mixed $mix_value A value to store associated with the key.
	 * @param boolean $boo_overwrite Whether to overwrite an existing property.
	 * @return void
	 */
	protected function setProperty($str_key, $mix_value, $boo_overwrite = true) {
		
        $boo_exists_key = $this->issetProperty($str_key);
			
		if(!$boo_exists_key || ($boo_exists_key && $boo_overwrite)) {
			$this->arr_properties[$str_key] = $mix_value;
		}
        
	}
    
	/**
	 * Returns a collection of key-value property pairs
	 * 
	 * Both parameters are not necessary, though the second parameter does allow
	 * users to extract all keys from the particular namespace.  The first 
	 * parameter defines a string needle to extract similar keys. 
	 * 
	 * For example, the properties may have a common prefix such as 'display_'.
	 * To extract all properties that have this keyword, but exclude all others,
	 * the method call would be:
	 * 
	 * $object->get_properties('display_');
	 * 
	 * @access public
	 * @param string $str_needle Defines a search needle to extract keys.
	 * @return array
	 */
	protected function getProperties() {
		return $this->arr_properties;
	}
    
    protected function getPropertiesMatching($str_needle) {
        
        $arr = array();
		
		if(is_string($str_needle) && !empty($str_needle)) {
            
            $arr = array();
            foreach($this->arr_properties as $str_key => $mix_value) {
                if(strpos($str_key, $str_needle) !== false) {
                    $arr[$str_key] = $mix_value;
                }
            }
            
		}
		
		return $arr;
    }
		
	/**
	 * Retrieves a specific property from the collection
	 * 
	 * @access public
	 * @param string $str_key A valid property key.
	 * @param mixed $mix_return Whether to return a particular value if property is
	 * 		not found, or an exception.
	 * @return mixed
	 * @throws acd_property_exception__property_not_found
	 */
	protected function getProperty($str_key, $mix_return = self::RETURN_EXCEPTION) {
		$mix = null;
		
		if(!$this->issetProperty($str_key)) {
			if($mix_return === self::RETURN_EXCEPTION) {
				throw new ClassStructure_Exception_PropertyDoesNotExist($str_key, $this);
			} else {
				return $mix_return;
			} 
		}
		
		$mix = $this->arr_properties[$str_key];
		
		return $mix;
	}
		
	/**
	 * Determines if a specific property key exists within the collection
	 * 
	 * @access public
	 * @param string $str_key An property key to test existence.
	 * @return boolean
	 */
	protected function issetProperty($str_key) {
        
		return array_key_exists($str_key, $this->arr_properties);

	}
    
    /**
     * Unsets a specific property key and destroys its value
     * 
     * @access protected
     * @param string $str_key An property key to test existence.
	 * @return void
     */
    protected function unsetProperty($str_key) {
		unset($this->arr_properties[$str_key]);
	}
    
    protected function clearProperties() {
        $this->arr_properties = array();
    }
		
}
?>
